﻿using System;
using System.Reflection;

using NUnit.Framework;

using WCSFContrib.Extensions.EventBroker;
using WCSFContrib.Extensions.CompositeWeb;

namespace WCSFContrib.Extensions.Tests.CompositeWeb
{
	[TestFixture]
	public class RequestEventBrokerTests
	{
		private RequestEventBroker sessionEventBroker;

		[TestFixtureSetUp]
		public void TestFixtureSetUp()
		{
			Type type = typeof(RequestEventBroker);
			ConstructorInfo[] constructors = type.GetConstructors(BindingFlags.NonPublic | BindingFlags.Instance);
			// Break into the class and instantiate it... Good for testing to not have to have a (session) instance.
			sessionEventBroker = (RequestEventBroker)constructors[0].Invoke(new object[0]);
		}

		[Test]
		public void Adding_Publisher_Subscriber_HooksUpCall()
		{
			string topic = "the topic";
			FooClass pubInstance = new FooClass();
			pubInstance.FooEventArgs = new EventArgs();
			BarClass subInstance = new BarClass();

			// First Pub
			sessionEventBroker.AddPublisher(pubInstance, new MemberReference<EventInfo>(topic, typeof(FooClass).GetEvent("FooEvent")));
			// Then Sub
			sessionEventBroker.AddSubscriber(subInstance, new MemberReference<MethodInfo>(topic, typeof(BarClass).GetMethod("FooHandler")));

			pubInstance.OnFoo();

			Assert.AreEqual(1, subInstance.FooHandlerCalls);
			Assert.AreEqual(pubInstance, subInstance.FooHandlerSender);
			Assert.AreEqual(pubInstance.FooEventArgs, subInstance.FooHandlerArgs);
		}

		[Test]
		public void Adding_Subscriber_Publisher_HooksUpCall()
		{
			string topic = "the topic";
			FooClass pubInstance = new FooClass();
			pubInstance.FooEventArgs = new EventArgs();
			BarClass subInstance = new BarClass();

			// First Sub
			sessionEventBroker.AddSubscriber(subInstance, new MemberReference<MethodInfo>(topic, typeof(BarClass).GetMethod("FooHandler")));
			// Then Pub
			sessionEventBroker.AddPublisher(pubInstance, new MemberReference<EventInfo>(topic, typeof(FooClass).GetEvent("FooEvent")));

			Assert.AreEqual(0, subInstance.FooHandlerCalls);

			pubInstance.OnFoo();

			Assert.AreEqual(1, subInstance.FooHandlerCalls);
			Assert.AreEqual(pubInstance, subInstance.FooHandlerSender);
			Assert.AreEqual(pubInstance.FooEventArgs, subInstance.FooHandlerArgs);
		}

		[Test]
		public void SubscriberHasTwoListnersOnSameTopic()
		{
			string topic = "the topic";
			FooClass pubInstance = new FooClass();
			pubInstance.FooEventArgs = new EventArgs();
			TwoHandlersSubscriber subInstance = new TwoHandlersSubscriber();

			sessionEventBroker.AddPublisher(pubInstance, new MemberReference<EventInfo>(topic, typeof(FooClass).GetEvent("FooEvent")));

			sessionEventBroker.AddSubscriber(subInstance, new MemberReference<MethodInfo>(topic, typeof(TwoHandlersSubscriber).GetMethod("Listner1")));
			sessionEventBroker.AddSubscriber(subInstance, new MemberReference<MethodInfo>(topic, typeof(TwoHandlersSubscriber).GetMethod("Listner2")));

			Assert.AreEqual(0, subInstance.Listner1Count);
			Assert.AreEqual(0, subInstance.Listner2Count);

			pubInstance.OnFoo();

			Assert.AreEqual(1, subInstance.Listner1Count);
			Assert.AreEqual(1, subInstance.Listner2Count);
		}

		private const string FooTopic = "When Foo Happens!";

		private class FooClass
		{
			[EventPublication(FooTopic)]
			public event EventHandler FooEvent;
			public EventArgs FooEventArgs;

			public void OnFoo()
			{
				FooEvent.Invoke(this, FooEventArgs);
			}
		}

		private class BarClass
		{
			public object FooHandlerSender;
			public EventArgs FooHandlerArgs;
			public int FooHandlerCalls = 0;

			[EventSubscription(FooTopic)]
			public void FooHandler(object sender, EventArgs e)
			{
				FooHandlerCalls++;
				FooHandlerSender = sender;
				FooHandlerArgs = e;
			}
		}

		private class TwoHandlersSubscriber
		{
			public int Listner1Count = 0;
			public int Listner2Count = 0;

			[EventSubscription(FooTopic)]
			public void Listner1()
			{
				Listner1Count++;
			}

			[EventSubscription(FooTopic)]
			public void Listner2()
			{
				Listner2Count++;
			}
		}
	}
}